package zyz.framework.controller;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.support.RequestContext;
import zyz.framework.dao.dml.DmlItem;
import zyz.framework.dao.query.Condition;
import zyz.framework.dao.query.QueryItem;
import zyz.framework.dao.query.Where;
import zyz.framework.entity.BaseEntity;
import zyz.framework.entity.NameValue;
import zyz.framework.entity.Page;
import zyz.framework.entity.R;
import zyz.framework.exception.ServiceException;
import zyz.framework.service.BaseService;
import zyz.framework.util.DateUtil;
import zyz.framework.util.ReflectUtil;
import zyz.framework.util.StringUtils;

import javax.persistence.Table;
import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 基础controller,可共用
 */
public abstract class BaseController {
    protected Logger logger = Logger.getLogger(this.getClass());

    @Autowired
    public BaseService baseService;

    //查询条件
    public static final String EQ = Condition.EQ;
    public static final String GT = Condition.GT;
    public static final String GE = Condition.GE;
    public static final String LT = Condition.LT;
    public static final String LE = Condition.LE;
    public static final String IN = Condition.IN;
    public static final String NEQ = Condition.NEQ;
    public static final String LIKE = Condition.LIKE;
    public static final String NOT_IN = Condition.NOT_IN;
    public static final String LIKE_ALL = Condition.LIKE_ALL;
    public static final String NULL = Condition.NULL;
    public static final String NOT_NULL = Condition.NOT_NULL;

    protected DecimalFormat decimalFormat = new DecimalFormat("######0.00");

    /**
     * 获取session值
     *
     * @param attribueName
     * @return
     */
    public Object getSessionAttribute(String attribueName) {
        return this.getSession().getAttribute(attribueName);
    }

    /**
     * 获取session
     *
     * @return
     */
    public Session getSession() {
        return SecurityUtils.getSubject().getSession();
    }

    /**
     * 获取国际化信息
     *
     * @param code properites文件的Key
     * @return
     */
    public String getMessage(String code) {
        return this.getMessage(code, null);
    }

    /**
     * Session属性赋值
     *
     * @param attributeName  属性名
     * @param attributeValue 属性值
     */
    public void setSessionAtrribute(Object attributeName, Object attributeValue) {
        this.getSession().setAttribute(attributeName, attributeValue);
    }

    /**
     * 获取用户登陆IP
     *
     * @return
     * @throws Exception
     */
    public String getRemoteIp() throws Exception {
        HttpServletRequest request = this.getRequest();
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null) {
            ip = ip.split("\\,")[0];
        } else if (request.getRemoteAddr() != null) {
            ip = request.getRemoteAddr().split("\\,")[0];
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }

    /**
     * 移除session
     *
     * @param attribueName
     */
    public void removeSessionAttribute(Object attribueName) {
        this.getSession().removeAttribute(attribueName);
    }

    /**
     * 获取国际化信息(带参数)
     *
     * @param code properites文件的Key
     * @param args 参数类表，如new String[]{"0", "1"}
     * @return
     */
    public String getMessage(String code, Object[] args) {
        RequestContext requestContext = new RequestContext(this.getRequest());
        return requestContext.getMessage(code, args);
    }

    /**
     * 获取request
     *
     * @return
     */
    public HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    /**
     * Controller不绑定参数，从HttpServletRequest获取提交的所有参数，默认情况下String类型的数据会以String[]形式提交
     *
     * @param convert 是否需要转换(把提交的String[]转为String)
     * @return
     */
    public Map<String, Object> getRequestMap(HttpServletRequest request, boolean convert) {
        Map<String, Object> result = new HashMap<String, Object>();

        //不转换
        if (!convert) {
            result.putAll(request.getParameterMap());
        }

        //获取提交的所有参数
        Map<String, String[]> paramMap = request.getParameterMap();
        for (String key : paramMap.keySet()) {
            //参数值
            Object value = paramMap.get(key);
            if (value == null) {
                continue;
            }

            //判断是否需要转换
            if (value instanceof String[]) {
                String[] valueArray = (String[]) value;

                if (valueArray != null && valueArray.length == 1) {
                    result.put(key, valueArray[0]);
                } else {
                    result.put(key, value);
                }
            } else {
                result.put(key, value);
            }
        }
        return result;
    }

    /**
     * 添加where条件到whereList
     *
     * @param whereList
     * @param column    数据库字段名
     * @param value     值
     */
    public void addWhereCondition(List<Where> whereList, String column, Object value) throws Exception {
        this.addWhereCondition(whereList, column, EQ, value, true);
    }

    /**
     * 添加where条件到whereList
     *
     * @param whereList
     * @param column    数据库字段名
     * @param value     值
     */
    public void addWhereCondition(List<Where> whereList, String column, String condtion, Object value) throws Exception {
        this.addWhereCondition(whereList, column, condtion, value, true);
    }

    /**
     * 添加where条件到whereList
     *
     * @param whereList
     * @param column           数据库字段名
     * @param value            值
     * @param isDateNeedCovert 时间是否需要转为long，默认为true
     */
    public void addWhereCondition(List<Where> whereList, String column, String condtion, Object value, boolean isDateNeedCovert) throws Exception {
        if (StringUtils.isEmpty(column) || value == null) {
            return;
        }
        if (value instanceof String && StringUtils.isEmpty(value.toString())) {
            return;
        }

        if (whereList == null) {
            whereList = new ArrayList<Where>();
        }
        if (isDateNeedCovert && value instanceof Date) {
            value = DateUtil.convert((Date) value);
        }
        whereList.add(new Where(column, value, condtion));
    }

    /**
     * 添加and条件，如：add_time > startValue and add_time < endValue
     *
     * @param whereList
     * @param column     数据库字段名
     * @param startValue
     * @param endValue
     */
    public void addAndWhereCondition(List<Where> whereList, String column, Object startValue, Object endValue) throws Exception {
        this.addAndWhereCondition(whereList, column, startValue, endValue, true);
    }

    /**
     * 添加and条件，如：add_time > startValue and add_time < endValue
     *
     * @param whereList
     * @param column     数据库字段名
     * @param startValue
     * @param endValue
     * @param formatType 格式化类型(date/datetime),默认为date
     */
    public void addAndWhereCondition(List<Where> whereList, String column, Object startValue, Object endValue, String formatType) throws Exception {
        this.addAndWhereCondition(whereList, column, startValue, endValue, true);
    }

    /**
     * 添加and条件，如：add_time > startValue and add_time < endValue
     *
     * @param whereList
     * @param column           数据库字段名
     * @param startValue
     * @param endValue
     * @param isDateNeedCovert 时间是否需要转为long，默认为true
     */
    public void addAndWhereCondition(List<Where> whereList, String column, Object startValue, Object endValue, boolean isDateNeedCovert) throws Exception {
        this.addAndWhereCondition(whereList, column, startValue, endValue, isDateNeedCovert, "date");
    }

    /**
     * 添加and条件，如：add_time > startValue and add_time < endValue
     *
     * @param whereList
     * @param column           数据库字段名
     * @param startValue
     * @param endValue
     * @param isDateNeedCovert 时间是否需要转为long，默认为true
     * @param formatType       格式化类型(date/datetime),默认为date
     */
    public void addAndWhereCondition(List<Where> whereList, String column, Object startValue, Object endValue, boolean isDateNeedCovert, String formatType) throws Exception {
        if (StringUtils.isEmpty(column)) {
            return;
        }

        if (whereList == null) {
            whereList = new ArrayList<Where>();
        }

        List<NameValue> ands = new ArrayList<NameValue>();
        if (startValue != null) {
            if (isDateNeedCovert && startValue instanceof Date) {
                if ("date".equals(formatType)) {
                    String startDate = DateUtil.dateFormat((Date) startValue) + " 00:00:00";
                    startValue = DateUtil.convert(startDate);
                } else {
                    startValue = DateUtil.convert((Date) endValue);
                }
            }
            ands.add(new NameValue(column, startValue, GE));
        }
        if (endValue != null) {
            if (isDateNeedCovert && endValue instanceof Date) {
                if ("date".equals(formatType)) {
                    String endDate = DateUtil.dateFormat((Date) endValue) + " 23:59:59";
                    endValue = DateUtil.convert(endDate);
                } else {
                    endValue = DateUtil.convert((Date) endValue);
                }
            }
            ands.add(new NameValue(column, endValue, LE));
        }
        if (ands.size() > 0) {
            whereList.add(new Where(ands));
        }
    }

    /**
     * 插入一条数据
     *
     * @param obj 主键默认“id”
     * @return
     * @throws Exception
     */
    public R insert(Object obj) throws Exception {
        return insert(obj, obj.getClass());
    }

    /**
     * 插入一条数据
     *
     * @param obj 主键默认“id”
     * @return
     * @throws Exception
     */
    public R insert(Object obj, String pkColumn) throws Exception {
        return insert(obj, obj.getClass(), pkColumn);
    }

    /**
     * 插入一条数据
     *
     * @param obj 主键默认“id”
     * @return
     * @throws Exception
     */
    public R insert(Object obj, Class<?> clazz) throws Exception {
        return insert(obj, clazz, null);
    }

    /**
     * 插入一条数据
     *
     * @param obj 主键默认“id”
     * @return
     * @throws Exception
     */
    public R insert(Object obj, Class<?> clazz, String pkColumn) throws Exception {
        R response = new R();
        String tableName = getTableName(clazz);
        if (tableName == null) {
            throw new ServiceException("表名不能为空");
        }
        if (obj.getClass().getName().equals(clazz.getName())) {
            Long result;
            if (pkColumn != null) {
                result = baseService.insert(pkColumn, obj);
            } else {
                result = baseService.insert(obj);
            }
            if (result > 0) {
                response.setId(result);
                response.setStatus(R.OK);
                response.setDescription("insert.success");
            } else {
                response.setStatus(R.ERROR);
                response.setDescription("insert.fail");
            }
            return response;
        }
        DmlItem dmlItem = null;
        if (obj instanceof DmlItem) {
            dmlItem = (DmlItem) obj;
        } else if (obj instanceof Map) {
            dmlItem = new DmlItem();
            List<NameValue> params = new ArrayList<NameValue>();
            Map<String, Object> map = (Map<String, Object>) obj;
            for (String key : map.keySet()) {
                params.add(new NameValue(key, map.get(key)));
            }
            dmlItem.setParams(params);
        }
        dmlItem.setTableName(tableName);
        if (pkColumn != null) {
            dmlItem.setPkColumn(pkColumn);
        }
        Long result = baseService.insert(dmlItem);
        if (result > 0) {
            response.setId(result);
            response.setStatus(R.OK);
            response.setDescription("insert.success");
        } else {
            response.setStatus(R.ERROR);
            response.setDescription("insert.fail");
        }

        return response;
    }


    /**
     * 根据主键更新数据
     *
     * @param obj
     * @return 主键默认“id”
     * @throws Exception
     */
    public R update(Object obj) throws Exception {
        return update(obj, obj.getClass());
    }

    /**
     * 根据主键更新数据
     *
     * @param obj
     * @return 主键默认“id”
     * @throws Exception
     */
    public R update(Object obj, Class<?> clazz) throws Exception {
        return update(obj, obj.getClass(), null);
    }

    /**
     * 根据主键更新数据
     *
     * @param obj
     * @return 主键默认“id”
     * @throws Exception
     */
    public R update(Object obj, String pkColumn) throws Exception {
        return update(obj, obj.getClass(), pkColumn);
    }

    /**
     * 根据主键更新数据
     *
     * @param obj
     * @param pkColumn 主键
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public R update(Object obj, Class<?> clazz, String pkColumn) throws Exception {
        R response = new R();


        if (obj.getClass().getName().equals(clazz.getName())) {
            Integer result;
            if (pkColumn != null) {
                result = baseService.updateById(pkColumn, obj, true);
            } else {
                result = baseService.updateById(obj, true);
            }
            if (result > 0) {
                response.setStatus(R.OK);
                response.setDescription("update.success");
            } else {
                response.setStatus(R.ERROR);
                response.setDescription("update.fail");
            }

            return response;
        }

        DmlItem dmlItem = null;
        if (obj instanceof DmlItem) {
            dmlItem = (DmlItem) obj;
        } else if (obj instanceof Map) {
            dmlItem = new DmlItem();
            List<NameValue> params = new ArrayList<NameValue>();
            Map<String, Object> map = (Map<String, Object>) obj;
            String id = pkColumn == null ? "id" : pkColumn;
            for (String key : map.keySet()) {
                if (key.equals(id)) {
                    continue;
                }
                params.add(new NameValue(key, map.get(key)));
            }
            dmlItem.setPkColumn(id);
            dmlItem.setId(map.get(id));
            dmlItem.setParams(params);
            dmlItem.setTableName(getTableName(clazz));
        }
        Integer result = baseService.update(dmlItem);
        if (result > 0) {
            response.setStatus(R.OK);
            response.setDescription("update.success");
        } else {
            response.setStatus(R.ERROR);
            response.setDescription("update.fail");
        }
        return response;
    }

    /**
     * 根据主键(id)删除数据
     *
     * @param id 要删除的记录ID
     * @return
     * @throws Exception
     */
    public R deleteById(String id, Class<?> clazz) throws Exception {
        return deleteById("id", id, clazz);
    }

    /**
     * 根据主键删除数据(主键字段不是id，要另外指定如：menu_id)
     *
     * @param pkColumn 数据库字段名
     * @param id       主键值
     * @return
     * @throws Exception
     */
    public R deleteById(String pkColumn, String id, Class<?> clazz) throws Exception {
        R response = new R();
        try {
            Integer result = baseService.deleteById(pkColumn, id, clazz);
            if (result > 0) {
                response.setStatus(R.OK);
                response.setDescription("delete.success");
            } else {
                response.setStatus(R.ERROR);
                response.setDescription("delete.fail");
            }
        } catch (Exception e) {
            logger.error(e.toString());
            response.setStatus(R.ERROR);
            response.setDescription("delete.fail");
        }

        return response;
    }

    /**
     * 删除
     *
     * @param dmlItem
     * @return
     * @throws Exception
     */
    public R delete(DmlItem dmlItem) throws Exception {
        R response = new R();
        Integer result = baseService.delete(dmlItem);
        if (result > 0) {
            response.setStatus(R.OK);
            response.setDescription("delete.success");
        } else {
            response.setStatus(R.ERROR);
            response.setDescription("delete.fail");
        }
        return response;
    }


    /**
     * 查询单条记录，返回Map
     *
     * @param queryItem
     * @param clazz     实体
     * @return
     * @throws Exception
     */
    public <T> T getOne(QueryItem queryItem, Class<T> clazz) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getOne(queryItem, clazz);
    }


    /**
     * 查询单条记录，返回BaseEntity
     *
     * @param queryItem
     * @param clazz
     * @return
     * @throws Exception
     */
    public Map getOne(QueryItem queryItem, Class<?> clazz, Class<Map> map) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getOne(queryItem, map);
    }


    /**
     * 查询列表，返回List
     *
     * @param queryItem
     * @return
     * @throws Exception
     */
    public <T> List<T> getList(QueryItem queryItem, Class<T> clazz) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getList(queryItem, clazz);
    }

    /**
     * 查询列表，返回List
     *
     * @param queryItem
     * @return
     * @throws Exception
     */
    public List<Map> getList(QueryItem queryItem, Class<?> clazz, Class<Map> map) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getList(queryItem, map);
    }

    /**
     * 查询列表，返回Page
     *
     * @param queryItem
     * @return
     * @throws Exception
     */
    public <T> Page<T> getPage(QueryItem queryItem, Class<T> clazz) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getPage(queryItem, clazz);
    }

    /**
     * 查询列表，返回Page
     *
     * @param queryItem
     * @return
     * @throws Exception
     */
    public Page<Map> getPage(QueryItem queryItem, Class<?> clazz, Class<Map> map) throws Exception {
        if (clazz == null) {
            throw new ServiceException("clazz can not be null");
        }
        queryItem.setTableName(getTableName(clazz));
        return baseService.getPage(queryItem, map);
    }

    /**
     * 非空校验
     *
     * @param obj(Map/BaseEntity) 要做校验的数据
     * @param columns             要做非空校验的字段
     * @param texts               提示信息
     * @return
     */
    public String validateNull(Object obj, String[] columns, String[] texts) {
        if (columns == null || texts == null || columns.length <= 0 || texts.length <= 0 || columns.length != texts.length) {
            return null;
        }
        String errorMsg = null;
        for (int i = 0; i < columns.length; i++) {
            String column = columns[i];
            Object value = null;
            if (obj instanceof Map) { //Map
                if (columns[i] == null) {
                    continue;
                }
                value = ((Map<String, Object>) obj).get(column);
            } else if (obj instanceof BaseEntity) { //BaseEntity
                if (columns[i] == null) {
                    continue;
                }
                value = ReflectUtil.getFieldValue(obj, column);
            } else if (obj instanceof Object[]) { //Object[]
                value = ((Object[]) obj)[i];
            }
            boolean isNull = false;
            if (value instanceof String) {
                if (value == null || StringUtils.isEmpty(value.toString())) {
                    isNull = true;
                }
            } else if (value instanceof Object[]) {
                if (value == null) {
                    isNull = true;
                } else {
                    Object[] arrayValue = (Object[]) value;
                    if (arrayValue.length <= 0 || arrayValue[0] == null) {
                        isNull = true;
                    } else if (arrayValue[0] instanceof String && StringUtils.isEmpty(arrayValue[0].toString())) {
                        isNull = true;
                    }
                }
            } else {
                if (value == null) {
                    isNull = true;
                }
            }

            if (isNull) {
                errorMsg = texts[i];
                break;
            }
        }
        return errorMsg;
    }

    /**
     * 获取表名
     */
    private String getTableName(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Table.class)) {
            Table table = clazz.getAnnotation(Table.class);
            if (table.name() != null) {
                return table.name();
            }
        }
        return null;
    }
}
